#pragma once

#include <pthread.h>
#include <functional>
#include "Sock.hpp"
#include "log.hpp"
#include "Protocol.hpp"

namespace Alpaca
{
    using namespace Protocal_ns;
    class TcpServer;

    using func_t = std::function<Response(const Request &)>;

    struct ThreadData
    {
        ThreadData(int s, const std::string &ip, const uint16_t &port, TcpServer *p)
            : _sock(s), _ip(ip), _port(port), tsvrp(p)
        {
        }

        ~ThreadData()
        {
        }

        int _sock;
        std::string _ip;
        uint16_t _port;
        TcpServer *tsvrp;
    };

    class TcpServer
    {
    public:
        TcpServer(func_t func, uint16_t port) : _func(func), _port(port)
        {
        }

        ~TcpServer()
        {
            _listensock.Close();
        }

        void InitServer()
        {
            _listensock.Socket();
            _listensock.Bind(_port);
            _listensock.Listhen();
            logmessage(INFO, "init server done,listensock %d", _listensock.Fd());
        }

        void Start()
        {
            while (true)
            {
                std::string clientip;
                uint16_t clientport;
                int sock = _listensock.Accept(&clientip, &clientport);
                if (sock < 0)
                    continue;

                logmessage(DEBUG, "get a new clientinfo : [%s %d]", clientip.c_str(), clientport);
                pthread_t pid;
                ThreadData *td = new ThreadData(sock, clientip, clientport, this);
                pthread_create(&pid, nullptr, ThreadRoutine, td);
            }
        }

        void ServiceIO(int sock, const std::string &ip, uint16_t &port)
        {
            // logmessage(DEBUG,"ready to IO");
            std::string inbuffer;
            while (true)
            {
                // 保证读一整个报文
                std::string package;
                int n = ReadPackage(sock, inbuffer, &package);
                if (n == -1)
                    return;
                else if (n == 0)
                    continue;
                else
                {
                    // 去掉报头c
                    package = RemoveHeader(package, n);

                    // 将读取到的数据恢复成结构体
                    Request rq;
                    rq.DeSerialize(package);

                    // 调用对应服务
                    std::cout << rq._x << " " << rq._y << " " << rq._op << std::endl;
                    Response resp = _func(rq);

                    // 序列化
                    std::string sendstr;
                    resp.Serialize(&sendstr);

                    // 添加报头
                    sendstr = AddHeader(sendstr);

                    // 数据发回客户端
                    send(sock, sendstr.c_str(), sendstr.size(), 0);
                }
            }
            close(sock);
        }

        static void *ThreadRoutine(void *args)
        {
            pthread_detach(pthread_self());
            ThreadData *td = static_cast<ThreadData *>(args);
            td->tsvrp->ServiceIO(td->_sock, td->_ip, td->_port);
            logmessage(DEBUG, "client quit...");
            delete td;
            return nullptr;
        }

    private:
        uint16_t _port;
        Sock _listensock;
        func_t _func;
    };
}
